package euler.helpers;

import static org.jlambda.Context._;
import static org.jlambda.util.Math.square;

import java.util.ArrayList;
import java.util.NoSuchElementException;

import org.jlambda.functions.Fun1;
import org.jlambda.functions.Fun2;
import org.jlambda.list.List;
import org.jlambda.list.MemoizingList;
import org.jlambda.listFunctions.ListFun1;
import org.jlambda.util.Lists;
import org.jlambda.util.Math;

public class Primes extends ListFun1<Long, Long> {

	// MemoizingList<Long> list;
	
	class ListMem {
		long val = 0;
		ListMem next = null;
	}
	
	ListMem head = null;
	ListMem tail = null;
	
//	 precalulated from previous runs, 3.5 secs to 219ms for 100000
	static ArrayList<Long> base = Lists.<Long>list(3l, 5l, 7l, 11l, 13l, 17l, 19l, 23l, 29l, 31l, 37l);//, 41l, 43l, 47l, 53l, 59l, 61l, 67l, 71l, 73l, 79l, 83l, 89l, 97l, 101l, 103l, 107l, 109l, 113l, 127l, 131l, 137l, 139l, 149l, 151l, 157l, 163l, 167l, 173l, 179l, 181l, 191l, 193l, 197l, 199l, 211l, 223l, 227l, 229l, 233l, 239l, 241l, 251l, 257l, 263l, 269l, 271l, 277l, 281l, 283l, 293l, 307l, 311l, 313l, 317l, 331l, 337l, 347l, 349l, 353l, 359l, 367l, 373l, 379l, 383l, 389l, 397l, 401l, 409l, 419l, 421l, 431l, 433l, 439l, 443l, 449l, 457l, 461l, 463l, 467l, 479l, 487l, 491l, 499l, 503l, 509l, 521l, 523l, 541l, 547l, 557l, 563l, 569l, 571l, 577l, 587l, 593l, 599l, 601l, 607l, 613l, 617l, 619l, 631l, 641l, 643l, 647l, 653l, 659l, 661l, 673l, 677l, 683l, 691l, 701l, 709l, 719l, 727l, 733l, 739l, 743l, 751l, 757l, 761l, 769l, 773l, 787l, 797l, 809l, 811l, 821l, 823l, 827l, 829l, 839l, 853l, 857l, 859l, 863l, 877l, 881l, 883l, 887l, 907l, 911l, 919l, 929l, 937l, 941l, 947l, 953l, 967l, 971l, 977l, 983l, 991l, 997l, 1009l, 1013l, 1019l, 1021l, 1031l, 1033l, 1039l, 1049l, 1051l, 1061l, 1063l, 1069l, 1087l, 1091l, 1093l, 1097l, 1103l, 1109l, 1117l, 1123l, 1129l, 1151l, 1153l, 1163l, 1171l, 1181l, 1187l, 1193l, 1201l, 1213l, 1217l, 1223l, 1229l, 1231l, 1237l, 1249l, 1259l, 1277l, 1279l, 1283l, 1289l, 1291l, 1297l, 1301l, 1303l, 1307l, 1319l, 1321l, 1327l, 1361l, 1367l, 1373l, 1381l, 1399l, 1409l, 1423l, 1427l, 1429l, 1433l, 1439l, 1447l, 1451l, 1453l, 1459l, 1471l, 1481l, 1483l, 1487l, 1489l, 1493l, 1499l, 1511l, 1523l, 1531l, 1543l, 1549l, 1553l, 1559l, 1567l, 1571l, 1579l, 1583l, 1597l, 1601l, 1607l, 1609l, 1613l, 1619l, 1621l, 1627l, 1637l, 1657l, 1663l, 1667l, 1669l, 1693l, 1697l, 1699l, 1709l, 1721l, 1723l, 1733l, 1741l, 1747l, 1753l, 1759l, 1777l, 1783l, 1787l, 1789l, 1801l, 1811l, 1823l, 1831l, 1847l, 1861l, 1867l, 1871l, 1873l, 1877l, 1879l, 1889l, 1901l, 1907l, 1913l, 1931l, 1933l, 1949l, 1951l, 1973l, 1979l, 1987l, 1993l, 1997l, 1999l, 2003l, 2011l, 2017l, 2027l, 2029l, 2039l, 2053l, 2063l, 2069l, 2081l, 2083l, 2087l, 2089l, 2099l, 2111l, 2113l, 2129l, 2131l, 2137l, 2141l, 2143l, 2153l, 2161l, 2179l, 2203l, 2207l, 2213l, 2221l, 2237l, 2239l, 2243l, 2251l, 2267l, 2269l, 2273l, 2281l, 2287l, 2293l, 2297l, 2309l, 2311l, 2333l, 2339l, 2341l, 2347l, 2351l, 2357l, 2371l, 2377l, 2381l, 2383l, 2389l, 2393l, 2399l, 2411l, 2417l, 2423l, 2437l, 2441l, 2447l, 2459l, 2467l, 2473l, 2477l, 2503l, 2521l, 2531l, 2539l, 2543l, 2549l, 2551l, 2557l, 2579l, 2591l, 2593l, 2609l, 2617l, 2621l, 2633l, 2647l, 2657l, 2659l, 2663l, 2671l, 2677l, 2683l, 2687l, 2689l, 2693l, 2699l, 2707l, 2711l, 2713l, 2719l, 2729l, 2731l, 2741l, 2749l, 2753l, 2767l, 2777l, 2789l, 2791l, 2797l, 2801l, 2803l, 2819l, 2833l, 2837l, 2843l, 2851l, 2857l, 2861l, 2879l, 2887l, 2897l, 2903l, 2909l, 2917l, 2927l, 2939l, 2953l, 2957l, 2963l, 2969l, 2971l, 2999l, 3001l, 3011l, 3019l, 3023l, 3037l, 3041l, 3049l, 3061l, 3067l, 3079l, 3083l, 3089l, 3109l, 3119l, 3121l, 3137l, 3163l, 3167l, 3169l, 3181l, 3187l, 3191l, 3203l, 3209l, 3217l, 3221l, 3229l, 3251l, 3253l, 3257l, 3259l, 3271l, 3299l, 3301l, 3307l, 3313l, 3319l, 3323l, 3329l, 3331l, 3343l, 3347l, 3359l, 3361l, 3371l, 3373l, 3389l, 3391l, 3407l, 3413l, 3433l, 3449l, 3457l, 3461l, 3463l, 3467l, 3469l, 3491l, 3499l, 3511l, 3517l, 3527l, 3529l, 3533l, 3539l, 3541l, 3547l, 3557l, 3559l, 3571l, 3581l, 3583l, 3593l, 3607l, 3613l, 3617l, 3623l, 3631l, 3637l, 3643l, 3659l, 3671l, 3673l, 3677l, 3691l, 3697l, 3701l, 3709l, 3719l, 3727l, 3733l, 3739l, 3761l, 3767l, 3769l, 3779l, 3793l, 3797l, 3803l, 3821l, 3823l, 3833l, 3847l, 3851l, 3853l, 3863l, 3877l, 3881l, 3889l, 3907l, 3911l, 3917l, 3919l, 3923l, 3929l, 3931l, 3943l, 3947l, 3967l, 3989l, 4001l, 4003l, 4007l, 4013l, 4019l, 4021l, 4027l, 4049l, 4051l, 4057l, 4073l, 4079l, 4091l, 4093l, 4099l, 4111l, 4127l, 4129l, 4133l, 4139l, 4153l, 4157l, 4159l, 4177l, 4201l, 4211l, 4217l, 4219l, 4229l, 4231l, 4241l, 4243l, 4253l, 4259l, 4261l, 4271l, 4273l, 4283l, 4289l, 4297l, 4327l, 4337l, 4339l, 4349l, 4357l, 4363l, 4373l, 4391l, 4397l, 4409l, 4421l, 4423l, 4441l, 4447l, 4451l, 4457l, 4463l, 4481l, 4483l, 4493l, 4507l, 4513l, 4517l, 4519l, 4523l, 4547l, 4549l, 4561l, 4567l, 4583l, 4591l, 4597l, 4603l, 4621l, 4637l, 4639l, 4643l, 4649l, 4651l, 4657l, 4663l, 4673l, 4679l, 4691l, 4703l, 4721l, 4723l, 4729l, 4733l, 4751l, 4759l, 4783l, 4787l, 4789l, 4793l, 4799l, 4801l, 4813l, 4817l, 4831l, 4861l, 4871l, 4877l, 4889l, 4903l, 4909l, 4919l, 4931l, 4933l, 4937l, 4943l, 4951l, 4957l, 4967l, 4969l, 4973l, 4987l, 4993l, 4999l, 5003l, 5009l, 5011l, 5021l, 5023l, 5039l, 5051l, 5059l, 5077l, 5081l, 5087l, 5099l, 5101l, 5107l, 5113l, 5119l, 5147l, 5153l, 5167l, 5171l, 5179l, 5189l, 5197l, 5209l, 5227l, 5231l, 5233l, 5237l, 5261l, 5273l, 5279l, 5281l, 5297l, 5303l, 5309l, 5323l, 5333l, 5347l, 5351l, 5381l, 5387l, 5393l, 5399l, 5407l, 5413l, 5417l, 5419l, 5431l, 5437l, 5441l, 5443l, 5449l, 5471l, 5477l, 5479l, 5483l, 5501l, 5503l, 5507l, 5519l, 5521l, 5527l, 5531l, 5557l, 5563l, 5569l, 5573l, 5581l, 5591l, 5623l, 5639l, 5641l, 5647l, 5651l, 5653l, 5657l, 5659l, 5669l, 5683l, 5689l, 5693l, 5701l, 5711l, 5717l, 5737l, 5741l, 5743l, 5749l, 5779l, 5783l, 5791l, 5801l, 5807l, 5813l, 5821l, 5827l, 5839l, 5843l, 5849l, 5851l, 5857l, 5861l, 5867l, 5869l, 5879l, 5881l, 5897l, 5903l, 5923l, 5927l, 5939l, 5953l, 5981l, 5987l, 6007l, 6011l, 6029l, 6037l, 6043l, 6047l, 6053l, 6067l, 6073l, 6079l, 6089l, 6091l, 6101l, 6113l, 6121l, 6131l, 6133l, 6143l, 6151l, 6163l, 6173l, 6197l, 6199l, 6203l, 6211l, 6217l, 6221l, 6229l, 6247l, 6257l, 6263l, 6269l, 6271l, 6277l, 6287l, 6299l, 6301l, 6311l, 6317l, 6323l, 6329l, 6337l, 6343l, 6353l, 6359l, 6361l, 6367l, 6373l, 6379l, 6389l, 6397l, 6421l, 6427l, 6449l, 6451l, 6469l, 6473l, 6481l, 6491l, 6521l, 6529l, 6547l, 6551l, 6553l, 6563l, 6569l, 6571l, 6577l, 6581l, 6599l, 6607l, 6619l, 6637l, 6653l, 6659l, 6661l, 6673l, 6679l, 6689l, 6691l, 6701l, 6703l, 6709l, 6719l, 6733l, 6737l, 6761l, 6763l, 6779l, 6781l, 6791l, 6793l, 6803l, 6823l, 6827l, 6829l, 6833l, 6841l, 6857l, 6863l, 6869l, 6871l, 6883l, 6899l, 6907l, 6911l, 6917l, 6947l, 6949l, 6959l, 6961l, 6967l, 6971l, 6977l, 6983l, 6991l, 6997l, 7001l, 7013l, 7019l, 7027l, 7039l, 7043l, 7057l, 7069l, 7079l, 7103l, 7109l, 7121l, 7127l, 7129l, 7151l, 7159l, 7177l, 7187l, 7193l, 7207l, 7211l, 7213l, 7219l, 7229l, 7237l, 7243l, 7247l, 7253l, 7283l, 7297l, 7307l, 7309l, 7321l, 7331l, 7333l, 7349l, 7351l, 7369l, 7393l, 7411l, 7417l, 7433l, 7451l, 7457l, 7459l, 7477l, 7481l, 7487l, 7489l, 7499l, 7507l, 7517l, 7523l, 7529l, 7537l, 7541l, 7547l, 7549l, 7559l, 7561l, 7573l, 7577l, 7583l, 7589l, 7591l, 7603l, 7607l, 7621l, 7639l, 7643l, 7649l, 7669l, 7673l, 7681l, 7687l, 7691l, 7699l, 7703l, 7717l, 7723l, 7727l, 7741l, 7753l, 7757l, 7759l, 7789l, 7793l, 7817l, 7823l, 7829l, 7841l, 7853l, 7867l, 7873l, 7877l, 7879l, 7883l, 7901l, 7907l, 7919l, 7927l, 7933l, 7937l, 7949l, 7951l, 7963l, 7993l, 8009l, 8011l, 8017l, 8039l, 8053l, 8059l, 8069l, 8081l, 8087l, 8089l, 8093l, 8101l, 8111l, 8117l, 8123l, 8147l, 8161l, 8167l, 8171l, 8179l, 8191l, 8209l, 8219l, 8221l, 8231l, 8233l, 8237l, 8243l, 8263l, 8269l, 8273l, 8287l, 8291l, 8293l, 8297l, 8311l, 8317l, 8329l, 8353l, 8363l, 8369l, 8377l, 8387l, 8389l, 8419l, 8423l, 8429l, 8431l, 8443l, 8447l, 8461l, 8467l, 8501l, 8513l, 8521l, 8527l, 8537l, 8539l, 8543l, 8563l, 8573l, 8581l, 8597l, 8599l, 8609l, 8623l, 8627l, 8629l, 8641l, 8647l, 8663l, 8669l, 8677l, 8681l, 8689l, 8693l, 8699l, 8707l, 8713l, 8719l, 8731l, 8737l, 8741l, 8747l, 8753l, 8761l, 8779l, 8783l, 8803l, 8807l, 8819l, 8821l, 8831l, 8837l, 8839l, 8849l, 8861l, 8863l, 8867l, 8887l, 8893l, 8923l, 8929l, 8933l, 8941l, 8951l, 8963l, 8969l, 8971l, 8999l, 9001l, 9007l, 9011l, 9013l, 9029l, 9041l, 9043l, 9049l, 9059l, 9067l, 9091l, 9103l, 9109l, 9127l, 9133l, 9137l, 9151l, 9157l, 9161l, 9173l, 9181l, 9187l, 9199l, 9203l, 9209l, 9221l, 9227l, 9239l, 9241l, 9257l, 9277l, 9281l, 9283l, 9293l, 9311l, 9319l, 9323l, 9337l, 9341l, 9343l, 9349l, 9371l, 9377l, 9391l, 9397l, 9403l, 9413l, 9419l, 9421l, 9431l, 9433l, 9437l, 9439l, 9461l, 9463l, 9467l, 9473l, 9479l, 9491l, 9497l, 9511l, 9521l, 9533l, 9539l, 9547l, 9551l, 9587l, 9601l, 9613l, 9619l, 9623l, 9629l, 9631l, 9643l, 9649l, 9661l, 9677l, 9679l, 9689l, 9697l, 9719l, 9721l, 9733l, 9739l, 9743l, 9749l, 9767l, 9769l, 9781l, 9787l, 9791l, 9803l, 9811l, 9817l, 9829l, 9833l, 9839l, 9851l, 9857l, 9859l, 9871l, 9883l, 9887l, 9901l, 9907l, 9923l, 9929l, 9931l, 9941l, 9949l, 9967l, 9973l);
	
	long last = 1;//res.get(res.size()-1).longValue();
	@Override
	protected void create(List<Long> list, Long hint) {
		((MemoizingList)list).append(2l);
//		((MemoizingList)list).append( 
//				res
//			);
		head = new ListMem();
		head.val = 2l;
		tail = head;
		for (Long val : base) {
			add(val);
		}
		
		// add eulers up to hint.
		Fun1<Number, Number> square41 = Fun2.compose(square, Math.add.curry(_,41));
		for (long i = 0; i < hint; i++) {
			long res = square41.apply(i).intValue();
			add(res + i);			
		}
	}
	
	private void add(long val){
		ListMem next = new ListMem();
		next.val = val;
		tail.next = next;
		tail = next;
	}
	
	private void insert(long val, ListMem before, ListMem after){
		ListMem newm = new ListMem();
		newm.val = val;
		newm.next = after;
		before.next = newm;
	}
	
	// borrowed from amry? caraban
	public Long getNext() {
		boolean isPrimeNumber = false;
		long i = last;
		
		long currentNumberToBeCompared = 1;
	    
		ListMem mem = null;
		ListMem prev = null;
		while(!isPrimeNumber) {
			i+=2;

		    isPrimeNumber = true;
			//Iterator<Long> calced = list.calculatedIterator();
		    // don't go above i
		    for(mem = head; mem.val < i; mem = mem.next) {
		    	prev = mem;
		    	try{
					currentNumberToBeCompared = mem.val; // dropped the while as its too expensive					
				} catch (NoSuchElementException e) {
					break;
				}
		        if (i % currentNumberToBeCompared == 0) {
		            isPrimeNumber = false;
		            break;
		        }		        
		        if (mem.next == null){
		        	break;
		        }
		    }
		    
		    if (isPrimeNumber) {
				for (long j = currentNumberToBeCompared + 1; j < i; j++) {
			        if (i % j == 0) {
			        	isPrimeNumber = false;
			            break;
			        }
			    }
		    }
		}
		
		// prime number
	    if (i < mem.val && prev!=null && prev!=mem) {
	    	// insert it into list
	    	insert(i, prev, mem);
	    } else {
	    	add(i);
	    }
		
        last = i;
        return last;
	}
}
